package com.whjz.dcfs.pub.excel;

import org.apache.commons.io.IOUtils;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Excel（XLSX)文件解析类，采用SAX基于事件的方式处理，可以有效的避免内存的消耗
 *
 *
 */
public final class XLSXParser implements IExcelParser {
    /** LOG */
    protected final static Logger LOG = LoggerFactory.getLogger(XLSXParser.class);

    /**
     * 将输入流解析为List集合
     * @param is 流
     * @return list
     */
    public List<List<String>> parse(InputStream is){
        List<List<String>> excelData = new ArrayList<List<String>>();
        InputStream sheet = null;
        try {
            OPCPackage pkg = OPCPackage.open(is);
            XSSFReader r = new XSSFReader(pkg);
            SharedStringsTable sst = r.getSharedStringsTable();
            XMLReader parser = XMLReaderFactory.createXMLReader();
            ContentHandler handler = new SheetHandler(sst, excelData);
            parser.setContentHandler(handler);

            // 该方法要求的传入参数为rId# 或 rSheet#
            InputStream sheet2 = r.getSheet("rId1");
            InputSource sheetSource = new InputSource(sheet2);
            parser.parse(sheetSource);
            sheet2.close();
        } catch (Exception e) {
            LOG.error("XLSX文件解析失败", e);
            throw new ExcelParseException("XLSX文件解析失败");
        } finally {
            if (sheet != null) {
                IOUtils.closeQuietly(sheet);
            }
        }
        return excelData;
    }

    /**
     * 将输入流解析为List集合
     * @return list
     */
    @Override
    public List<List<String>> parse(Sheet sheet){
    	return null;
    }
    /**
     * 基于SAX方式解析EXCEL文件，文件格式如下<row><c><v>123</v></c><c></c></row>
     * 需要注意的是如果单元格内容为空，则不存在<v></v>，因此需要在逻辑上进行区分;
     *
     *
     */
    private final static class SheetHandler extends DefaultHandler {
        private SharedStringsTable sst;

        private String lastContents;

        private boolean nextIsString;

        //单元格内容是否为空
        private boolean isNull;

        private List<List<String>> data;

        private List<String> currentRow;

        /**
         * constructor
         * @param sst SharedStringsTable
         * @param data 数据
         */
        private SheetHandler(SharedStringsTable sst, List<List<String>> data) {
            this.sst = sst;
            this.data = data;
        }

        /**
         * @param uri String
         * @param localName 本地名称
         * @param name 名称
         * @param attributes 属性
         * @throws SAXException 异常
         */
        public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
            if (EXCEL_ROW.equals(name)) {
                currentRow = new ArrayList<String>();
                data.add(currentRow);
            }
            if (EXCEL_CELL.equals(name)) {
                String cellType = attributes.getValue(EXCEL_CELL_TYPE);
                if (EXCEL_CELL_TYPE_STRING.equals(cellType)) {
                    nextIsString = true;
                    isNull = false;
                } else {
                    nextIsString = false;
                    isNull = true;
                }
            }
            if (EXCEL_CELL_CONTENT.equals(name)) {
                isNull = false;
            }
            // 遇到新标签时，清除内容
            lastContents = "";
        }

        /**
         * @param uri str
         * @param localName 名称
         * @param name 名称
         * @throws SAXException 异常
         */
        public void endElement(String uri, String localName, String name) throws SAXException {
            if (nextIsString) {
                int idx = Integer.parseInt(lastContents);
                lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString();
                nextIsString = false;
            }

            if (EXCEL_CELL_CONTENT.equals(name)) {
                currentRow.add(lastContents);
            } else if (EXCEL_CELL.equals(name) && isNull) {
                currentRow.add("");
                isNull = false;
            }
        }

        /**
         * @param ch 字符
         * @param start 起始位置
         * @param length 结束
         * @throws SAXException 异常捕获
         */
        public void characters(char[] ch, int start, int length) throws SAXException {
            lastContents += new String(ch, start, length);
        }
    }
}
